import React, { useCallback, useEffect, useRef } from 'react';
import styles from './index.less';
import G6 from '@antv/g6';
import { data } from './data';
import { models } from '@/.umi/plugin-model/Provider';

const r = 98;
const deg = 119.97;

const getAllNodes = (oList: any[], oId: string) => {
  let result: any[] = [];
  const getAll = (list: any[], id: string) => {
    const newIds: any[] = [];
    list.forEach((item: any) => {
      if (item.target === id) {
        newIds.push(item.source);
      } else if (item.source === id) {
        newIds.push(item.target)
      }
    });
    if (newIds.length > 0) {
      newIds.forEach((newId: string) => {
        result = [...result, newId];
        getAll(list.filter((l: any) => !((newIds.includes(l.source) && l.target === id) || (newIds.includes(l.target) && l.source === id))), newId);
      });
    }
  }
  getAll(oList, oId);
  return result;
}

const getBloodNodes = (oList: any[], oId: string) => {
  let result: any[] = [];
  const getBlood = (list: any[], id: string) => {
    const newIds: any[] = [];
    list.forEach((item: any) => {
      if (item.target === id && item.label === '影响') {
        newIds.push(item.source);
      }
    });
    if (newIds.length > 0) {
      newIds.forEach((newId: string) => {
        result = [...result, newId];
        getBlood(list.filter((l: any) => !(newIds.includes(l.source) && l.target === id)), newId);
      })
    }
  }
  getBlood(oList, oId);
  return result;
}
 

const getEffectNodes = (oList: any[], oId: string) => {
  let result: any[] = [];
  const getEffect = (list: any[], id: string) => {
    const newIds: any[] = [];
    list.forEach((item: any) => {
      if (item.source === id) {
        newIds.push(item.target);
      }
    });
    if (newIds.length > 0) {
      newIds.forEach((newId: string) => {
        result = [...result, newId];
        getEffect(list.filter((l: any) => !(newIds.includes(l.target) && l.source === id)), newId);
      })
    }
  }
  getEffect(oList, oId);
  return result;
}

const getAllColNodes = (oList: any[], oId: string) => {
  let result: any[] = [];
  const getAllCol = (list: any[], id: string) => {
    const newIds: any[] = [];
    list.forEach((item: any) => {
      if (item.isCol) {
        if (item.source === id) {
          newIds.push(item.target);
        } else if (item.target === id) {
          newIds.push(item.source);
        }
      }
      
    });
    if (newIds.length > 0) {
      newIds.forEach((newId: string) => {
        result = [...result, newId];
        getAllCol(list.filter((l: any) => !((newIds.includes(l.source) && l.target === id) || (newIds.includes(l.target) && l.source === id))), newId);
      })
    }
  }
  getAllCol(oList, oId);
  return result;
}


export default function IndexPage() {
  const graphRef = useRef<any>(null);
  const rendergraph = useCallback(() => {
 
    const container = document.getElementById('container');
    const width = container.scrollWidth;
    const height = (container.scrollHeight || 500);
    const { nodes: allNodes, edges: allEdges } = data;
    const nodesTmp: any[] = [];
    const edgesTmp: any[] = [];
    const allNodeIds: any[] = [];
    const includeEdges: any[] = [];
    allNodes.forEach((item: any) => {
      if (!item.parentId) {
        nodesTmp.push({
          label: item.name,
          ...item,
          size: 208,
          showBlood: true,
          showEffect: true,
        });
        allNodeIds.push(item.id);
      }
     
    });
    allEdges.forEach((item: any) => {
      if (allNodeIds.includes(item.source) || allNodeIds.includes(item.target)) {
        edgesTmp.push({
          ...item,
          label: '影响',
        });
      }
    });

    // 定义 Dom 节点
    // G6.registerNode('dom-node-1', {
    //   draw: (cfg: any, group: any) => {
    //     return group.addShape('dom', {
    //       attrs: {
    //         width: 208,
    //         height: 208,
    //         html: `
    //         <div class="node ${cfg.selected ? 'hover' : ''}" data-id="${cfg.id}">
    //         <div class="sector">
    //           <div class="content" data-id="${cfg.id}" id="content${cfg.id}">${cfg.label}</div>
    //           <div class="line1"></div>
    //           <div class="line2"></div>
    //           <div class="line3"></div>
    //           <div class="blood" data-id="${cfg.id}" id="blood${cfg.id}">
    //             <img src="/blood.png"/>
    //             <div class="sx1"></div>
    //             <div class="sx2"></div>
    //           </div>
    //           <div class="col" data-id="${cfg.id}" id="col${cfg.id}">
    //             <img src="/col.png"/>
    //             <div class="sx1"></div>
    //             <div class="sx2"></div>
    //           </div>
    //           <div class="effect" data-id="${cfg.id}" id="effect${cfg.id}">
    //             <img src="/effect.png" />
    //             <div class="sx1"></div>
    //             <div class="sx2"></div>
    //           </div>
    //         </div>
    //       </div>
    //            `,
    //       },
    //       draggable: true,
    //     });
    //   },
    //   getAnchorPoints() {
    //     return [
    //       [0.25, 0.5], // 左侧中间
    //       [0.75, 0.5], // 右侧中间
    //     ];
    //   },
    // });


    G6.registerNode('svg-node', {
      draw: (cfg: any, group: any) => {
        const { selected, hoverPath, showCol, showBlood, showEffect } = cfg;

        if (selected) {
          group.addShape('circle', {
            attrs: {
              x: 100,
              y: 100,
              r: 78,
              fill: '#5CD1FF',
              stroke: 'none',
              opacity: 0.54,
            },
            draggable: true
          });
          group.addShape('path', {
            attrs: {
              path: [
                ['M', 101.5, 100 - r + 1],
                ['A', r, r, 0, 0, 1, 100+Math.cos(deg) * r,100+Math.sin(deg) * r]
              ],
              stroke: (hoverPath === 'col' || showCol) ? '#DDD' : '#F3F3F3',
              lineWidth: 40,
              fill: 'none',
              class: 'path',
            },
            name: 'col',
            draggable: true
          });
  
          group.addShape('path', {
            attrs: {
              path: [
                ['M', 99.5, 100 - r + 1],
                ['A', r, r, 0, 0, 0, 100-Math.cos(deg) * r,100+Math.sin(deg) * r]
              ],
              stroke: (hoverPath === 'blood' || showBlood) ? '#DDD' : '#F3F3F3',
              lineWidth: 40,
              fill: 'none',
              class: 'path',
            },
            name: 'blood',
            draggable: true
          });
  
          group.addShape('path', {
            attrs: {
              path: [
                ['M', 100-Math.cos(120) * r, 100+Math.sin(120) * r],
                ['A', r, r, 0, 0, 0, 100+Math.cos(120) * r,100+Math.sin(120) * r]
              ],
              stroke: (hoverPath === 'effect' || showEffect) ? '#DDD' : '#F3F3F3',
              lineWidth: 40,
              fill: 'none',
              class: 'path',
            },
            name: 'effect',
            draggable: true
          });
  
          group.addShape('image', {
            attrs: {
              x: 184,
              y: 70,
              img: (hoverPath === 'col' || showCol) ? 'col_active.png' : 'col.png',
              width: 18,
              height: 18,
            },
            name: 'col',
            draggable: true
          });
  
          group.addShape('image', {
            attrs: {
              x: 2,
              y: 70,
              img: (hoverPath === 'blood' || showBlood) ? 'blood_active.png' : 'blood.png',
              width: 18,
              height: 18,
            },
            name: 'blood',
            draggable: true
          });
  
          group.addShape('image', {
            attrs: {
              x: 92,
              y: 190,
              img: (hoverPath === 'effect' || showEffect) ? 'effect_active.png' : 'effect.png',
              width: 18,
              height: 18,
            },
            name: 'effect',
            draggable: true
          });
        }

        const shape = group.addShape('circle', {
          attrs: {
            x: 100,
            y: 100,
            r: selected ? 68 : 50,
            fill: '#3789FF',
            class: 'circle',
            stroke: '#1881FF',
            lineWidth: 2,
          },
          name: 'circle-node',
          draggable: true
        });

        group.addShape('text', {
          attrs: {
            fill: '#fff',
            x: 60,
            y: 96,
            text: cfg.label.substr(0, 10),
          },
          draggable: true
        });

        if (cfg.label.length > 10) {
          group.addShape('text', {
            attrs: {
              fill: '#fff',
              x: 60,
              y: 116,
              text: cfg.label.length > 20 ? `${cfg.label.substr(10, 9)}...` : cfg.label.substr(10, 10),
            },
            draggable: true
          });
        }

        return shape;
      },
      // getAnchorPoints() {
      //   return [
      //     [0, 0.5], // 左侧中间
      //     [1, 0.5], // 右侧中间
      //   ];
      // },
    })

    G6.registerEdge(
      'can-running-g',
      {
        setState: (name: any, value: any, item: any) => {
          const shape = item.get('keyShape');
          const { style: { stroke } } = item.getModel();
          // lineDash 的差值，可以在后面提供 util 方法自动计算
          if (name === 'running') {
            if (value) {
              shape.attr('stroke', stroke);
              shape.attr('fill', stroke);
              shape.attr('lineWidth', 1.5);
              shape.attr('opacity', 1);
              shape.attr('endArrow', {
                path: 'M 6,0 L 6,0 L 0,4 L 0,-4 Z',
                d: 6,
              });
            } else {
              shape.attr('stroke', stroke);
              shape.attr('fill', stroke);
              shape.attr('lineWidth', 1);
              shape.attr('opacity', 0.6);
              shape.attr('endArrow', {
                path: 'M 6,0 L 6,0 L 0,4 L 0,-4 Z',
                d: 6,
              });
            }
          }
        },
      },
      'line',
    );

    
    if (graphRef.current) {
      graphRef.current.clear();
    }

    const tooltip = new G6.Tooltip({
      // offsetX: 10,
      // offsetY: 10,
      // the types of items that allow the tooltip show up
      // 允许出现 tooltip 的 item 类型
      itemTypes: ['edge', 'node'],
      // custom the tooltip's content
      // 自定义 tooltip 内容
      getContent: (e: any) => {
        const type = e.item.getType();
        const model = e.item.getModel();
        const outDiv = document.createElement('div');
        outDiv.style.width = 'fit-content';
        //outDiv.style.padding = '0px 0px 20px 0px';
        if (type === 'node') {
          outDiv.innerHTML = `
            <div>字段英文名: ${model.name}</div>`;
        } else {
          outDiv.innerHTML = `
            <div>JobId: ${model.id}</div>`;
        }
       
        return outDiv;
      },
      shouldBegin: (e: any) => e.item.getModel().parentId || e.item.getModel().source,
    });

    const minimap = new G6.Minimap({
      size: [150, 100],
    });

    graphRef.current = new G6.Graph({
      container: 'container',
      width,
      height,
      fitCenter: true,
      // fitView: true,
      autoPaint: true,
      plugins: [tooltip],
      modes: {
        default: ['drag-node', 'drag-canvas', 
        {
          type: 'zoom-canvas',
          fixSelectedItems: {
            fixAll: true,
          },
        },],
      },
      renderer: 'svg', // 使用 Dom node 的时候需要使用 svg 的渲染形势
      defaultNode: {
        type: 'svg-node',
      },
      // defaultEdge: {
      //   style: {
      //     endArrow: true,
      //     stroke: '#FF744F',
      //     lineWidth: 1,
      //   },
      // },
      defaultEdge: {
        type: 'can-running-g',
        style: {
          stroke: '#FF744F',
          endArrow: {
            path: 'M 6,0 L 6,0 L 0,4 L 0,-4 Z',
            d: 6,
          },
          fill: '#FF744F',
          opacity: 0.6,
        },
      },
      layout: {
        // type: 'radial',
        // preventOverlap: true,
        // strictRadial: false,
        // nodeSpacing: 50,
        type: 'force',
        linkDistance: 100, 
        nodeStrength: 100,
        preventOverlap: true,
        nodeSize: 240,
        // type: 'dagre',
        // rankdir: 'LR',

        // nodesepFunc: () => 10,
        // ranksepFunc: () => 10,
      },
    });

    const obj = {
      nodes: nodesTmp,
      edges: edgesTmp,
    };

    graphRef.current.data({
      ...obj,
    });
    graphRef.current.render();

    const lineRuning = (node: any, running: boolean) => {
      // console.log(ev)
      // eslint-disable-next-line no-underscore-dangle
      const edges = node.getEdges();
      edges.forEach((edge: any) => {
        // edge.toFront();
        graphRef.current.setItemState(edge, 'running', running);
      });
    };

    graphRef.current.on('node:click', (e: any) => {
      const { type, cfg: { name } } = e.target;
      const model = e.item.getModel();
      const { nodes, edges } = graphRef.current.save();
      if (type === 'circle' || type === 'text') {
        nodes.forEach((node: any) => {
          graphRef.current.updateItem(node.id, {
            ...node,
            selected: node.id === model.id ? !model.selected : false,
            hoverPath: '',
          });
        });
        
      } else if (type === 'path' || type === 'image') {
      
        let nodesNew: any[] = [];
        let edgesNew: any[] = [];
        if (name === 'col') {
          const { id } = model;
          const children = allNodes.filter((item: any) => item.parentId && item.parentId === id);
          const childIds = children.map((item: any) => item.id);
          const showParentIds = nodes.filter((item: any) => !item.parentId).map((item: any) => item.id);

          let result: any[] = [];

          childIds.forEach((child: string) => {
            result = Array.from(new Set([...result, ...getAllNodes(allEdges, child)]));
          });

          console.log('all cols:', result);
          
          const allChildren = allNodes.filter((item: any) => item.parentId && showParentIds.includes(item.parentId) && result.includes(item.id));
          result = allChildren.map((item: any) => item.id);

          if (children.length) {
            if (!model.showCol) {
              nodesNew = [...nodes];
              edgesNew = [...edges];
              children.forEach((child: any) =>{
                nodesNew.push({
                  ...child,
                  label: child.name.length > 14 ? `${child.name.substr(0, 14)}...` : child.name.substr(0, 14),
                  type: 'circle',
                  size: 100,
                  style: {
                    fill: '#65C358',
                    stroke: '#2CAB1A',
                    lineWidth: 2,
                  },
                  labelCfg: {
                    style: {
                      fill: '#fff',
                    },
                  },
                  // anchorPoints: [
                  //   [0, 0.5], // 左侧中间
                  //   [1, 0.5], // 右侧中间
                  // ],
                });
                edgesNew.push({
                  source: child.id,
                  target: model.id,
                  label: '包含',
                  style: {
                    stroke: '#3789FF',
                    fill: '#3789FF',
                  }
                });

                if (!includeEdges.some((e: any) => e.source === child.id && e.target === model.id)) {
                  includeEdges.push({
                    source: child.id,
                    target: model.id,
                  })
                }

              });

              allChildren.forEach((child: any) => {
                nodesNew.push({
                  ...child,
                  label: child.name.length > 14 ? `${child.name.substr(0, 14)}...` : child.name.substr(0, 14),
                  type: 'circle',
                  size: 100,
                  style: {
                    fill: '#65C358',
                    stroke: '#2CAB1A',
                    lineWidth: 2,
                  },
                  labelCfg: {
                    style: {
                      fill: '#fff',
                    },
                  },
                });
                edgesNew.push({
                  source: child.id,
                  target: child.parentId,
                  label: '包含',
                  style: {
                    stroke: '#3789FF',
                    fill: '#3789FF',
                  }
                });
                if (!includeEdges.some((e: any) => e.source === child.id && e.target === child.parentId)) {
                  includeEdges.push({
                    source: child.id,
                    target: child.parentId,
                  })
                }
              });

              allEdges.forEach((edge: any) => {
                if (result.includes(edge.source) || result.includes(edge.target)) {
                  edgesNew.push({
                    ...edge,
                    label: '影响',
                    style: {
                      stroke: '#FF744F',
                      fill: '#FF744F',
                    }
                  });
                }
              });

            } else {
              nodesNew = nodes.filter((item: any) => !result.includes(item.id) && !childIds.includes(item.id));
              edgesNew = edges.filter((item: any) => !result.includes(item.source) && !result.includes(item.target) && !childIds.includes(item.source) && !childIds.includes(item.target));
            }
            graphRef.current.updateItem(e.item, {
              ...model,
              showCol: !model.showCol,
            });
          } else {
            nodesNew = [...nodes];
            edgesNew = [...edges];
          }
        } else if (name === 'blood') {
          nodesNew = [...nodes];
          edgesNew = [...edges];
       
          if (model.showBlood) {
            const result = getBloodNodes(edges, model.id);
            console.log('blood ids:', result);
            if (result.length > 0) {
              nodesNew = nodes.filter((node: any) => !result.includes(node.id));
              edgesNew = edges.filter((edge: any) => !result.includes(edge.source) && !result.includes(edge.target));
            }
          } else {
            const result = getBloodNodes(edgesTmp, model.id);
            console.log('blood ids:', result);
            if (result.length > 0) {
              nodesNew = [...nodes, ...nodesTmp.filter((node: any) => result.includes(node.id))];
              edgesNew = [...edges, ...edgesTmp.filter((edge: any) => result.includes(edge.source) || result.includes(edge.target))];
            }
          }
          
          graphRef.current.updateItem(e.item, {
            ...model,
            showBlood: !model.showBlood,
          });
        } else {
          nodesNew = [...nodes];
          edgesNew = [...edges];
         
          if (model.showEffect) {
            const result = getEffectNodes(edges, model.id);
            console.log('effect ids:', result);
            if (result.length > 0) {
              nodesNew = nodes.filter((node: any) => !result.includes(node.id));
              edgesNew = edges.filter((edge: any) => !result.includes(edge.source) && !result.includes(edge.target));
            }
          } else {
            const result = getEffectNodes(edgesTmp, model.id);
            console.log('effect ids:', result);
            if (result.length > 0) {
              nodesNew = [...nodes, ...nodesTmp.filter((node: any) => result.includes(node.id))];
              edgesNew = [...edges, ...edgesTmp.filter((edge: any) => result.includes(edge.source) || result.includes(edge.target))];
            }
          }
          
          graphRef.current.updateItem(e.item, {
            ...model,
            showEffect: !model.showEffect,
          });
        }
        graphRef.current.changeData({
          nodes: nodesNew,
          edges: edgesNew,
        });
      }
    });

    graphRef.current.on('node:mouseover', (e: any) => {
      const { type, cfg: { name } } = e.target;
      const model = e.item.getModel();
      if ((type === 'path' || type === 'image') && model.hoverPath !== name) {
        graphRef.current.updateItem(e.item, {
          ...model,
          hoverPath: name,
        });
      }
      lineRuning(e.item, true);
      if (model.parentId) {
        const result = getAllColNodes([...allEdges, ...includeEdges], model.id);
        result.forEach((id: any) => {
          const node = graphRef.current.findById(id);
          if (node) {
            lineRuning(node, true);
          }
        });
      }
    });

    graphRef.current.on('node:mouseleave', (e: any) => {
      const model = e.item.getModel();
      graphRef.current.updateItem(e.item, {
        ...model,
        hoverPath: '',
      });
      lineRuning(e.item, false);
      if (model.parentId) {
        const result = getAllColNodes([...allEdges], model.id);
        result.forEach((id: any) => {
          const node = graphRef.current.findById(id);
          if (node) {
            lineRuning(node, false);
          }
        });
      }
    });

    if (typeof window !== 'undefined')
      window.onresize = () => {
        if (!graphRef.current || graphRef.current.get('destroyed')) return;
        if (!container || !container.scrollWidth || !container.scrollHeight) return;
        graphRef.current.changeSize(container.scrollWidth, container.scrollHeight);
    };
  }, [data]);

  useEffect(() => {
    rendergraph();
  }, []);

  return (
    <>
    <div className={styles.container} id="container"></div>
    {/* <svg x="500" y="800" width="300" height="300"  version="1.1" xmlns="http://www.w3.org/2000/svg">
      <g className={styles.g}>
        <circle className={styles.circle} cx="100" cy="100" r="50" fill="#00ADFF" />
        <path className={styles.path} d={`M 101.5 ${100 - r + 1} A ${r} ${r}, 0, 0, 1, ${100+Math.cos(deg) * r},${100+Math.sin(deg) * r}`} stroke="#F3F3F3" stroke-width="40" fill="none" />
        <image href="col.png" width="18" height="18" x="184" y="70"/>
        <path className={styles.path} d={`M 99.5 ${100 - r + 1} A ${r} ${r}, 0, 0, 0, ${100-Math.cos(deg) * r},${100+Math.sin(deg) * r}`} stroke="#F3F3F3" stroke-width="40" fill="none" />
        <image href="blood.png" width="18" height="18" x="2" y="70"/>
        <path className={styles.path} d={`M ${100-Math.cos(120) * r},${100+Math.sin(120) * r} A ${r} ${r}, 0, 0, 0, ${100+Math.cos(120) * r},${100+Math.sin(120) * r}`} stroke="#F3F3F3" stroke-width="40" fill="none" />
        <image href="effect.png" width="18" height="18" x="92" y="190"/>
        <text x="60" y="70" fill="#fff" font-size="12">T_CUSTOMER_INFO</text>
      </g>
    </svg> */}
    </>
  );
}
